Skip to content

fix(ci): pin semantic-release tooling and restore v0.95.0 notes#989

Open
nielspardon wants to merge 2 commits into
substrait-io:mainfrom
nielspardon:semantic-release-fix
Open

fix(ci): pin semantic-release tooling and restore v0.95.0 notes#989
nielspardon wants to merge 2 commits into
substrait-io:mainfrom
nielspardon:semantic-release-fix

Conversation

@nielspardon

Copy link
Copy Markdown
Member

Summary

Fixes semantic-release changelog generation and restores the missing v0.95.0 changelog entry.

Changes

Root cause

The empty v0.95.0 release notes were caused by floating release-tool dependencies. In particular, conventional-changelog-conventionalcommits@10.x regressed changelog generation in this setup, while 9.3.1 still generated the expected sections.

Validation

  • reproduced the empty-notes behavior with the floating toolchain
  • verified that pinning conventional-changelog-conventionalcommits@9.3.1 restores proper release notes generation
  • validated the trailer-stripping transform with a local semantic-release run against the v0.95.0 release range

@nielspardon nielspardon force-pushed the semantic-release-fix branch from ee42798 to b319460 Compare July 6, 2026 14:45
@nielspardon

Copy link
Copy Markdown
Member Author

This also ports the fix for stripping git trailers from substrait-io/substrait#1099 to substrait-java

@nielspardon nielspardon force-pushed the semantic-release-fix branch from b319460 to cbe6e12 Compare July 6, 2026 14:49
@nielspardon nielspardon force-pushed the semantic-release-fix branch from cbe6e12 to a44e1d2 Compare July 6, 2026 14:53
@nielspardon nielspardon marked this pull request as ready for review July 6, 2026 15:46

@bestbeforetoday bestbeforetoday left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need the RELEASE_DRY_RUN environment variable and programmatic manipulation of the semantic release configuration in addition to the standard --dry-run command-line argument? This already skips publishing, and it looks like the programmatic configuration manipulates (removes) publishing setting that (I guess) should not be used with --dry-run anyway. Without manually testing it is hard for me to tell for sure.

Comment thread ci/release/run.sh Outdated
-p "semantic-release@25.0.5" \
-p "@semantic-release/commit-analyzer@13.0.1" \
-p "@semantic-release/release-notes-generator@14.1.1" \
-p "@semantic-release/changelog@7.0.0-beta.1" \

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a prerelease beta version does not seem ideal unless there is some reason that release versions are not compatible or do not include some essential functionality. The latest recommended release is currently 6.0.3.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — there's no reason to be on the beta here. @semantic-release/changelog@6.0.3 is the current latest dist-tag and declares semantic-release >=18.0.0 as its peer dependency, so it's compatible with the pinned semantic-release@25.0.5. I've pinned 6.0.3 in both run.sh and dry_run.sh and validated it with a dry run: the preset resolves cleanly and the notes render correctly (0.95.00.95.1).

Comment thread .releaserc.mjs
Comment on lines +15 to +29
import { createRequire } from "node:module";
import { pathToFileURL } from "node:url";

const loadPreset = async () => {
try {
return (await import("conventional-changelog-conventionalcommits")).default;
} catch {
const require = createRequire(pathToFileURL(process.argv[1]));
const resolved = pathToFileURL(
require.resolve("conventional-changelog-conventionalcommits")
).href;
return (await import(resolved)).default;
}
};
const conventionalcommits = await loadPreset();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure why the import handling is so complicated here. The examples in the documentation are much simpler, using static rather than dynamic imports, and more what I would expect for ESM imports.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dynamic import + fallback is unfortunately load-bearing given how we invoke the tool. The release scripts run semantic-release via npx -p …, which installs conventional-changelog-conventionalcommits into a temporary node_modules alongside the semantic-release binary. A bare static import from this config resolves relative to the config file's own directory (the repo root, which has no node_modules) and fails with ERR_MODULE_NOT_FOUND — so the catch re-resolves the preset relative to the running semantic-release binary (process.argv[1]). The static-import example in the docs assumes the preset is installed as a local devDependency, which isn't the case here.

This was ported from the spec repo but the explanatory comment got dropped in the port — I've restored it so the "why" lives next to the code.

…nfig

Address review feedback on the semantic-release config:

- pin @semantic-release/changelog to the stable 6.0.3 (latest, peer
  semantic-release >=18.0.0) instead of 7.0.0-beta.1; there is no
  functional reason for the prerelease. Validated with a dry run.
- restore the explanatory comments that were dropped when the config
  was ported from the spec repo: why the preset import needs the npx
  temp-node_modules fallback, and why the publishing plugins are omitted
  via RELEASE_DRY_RUN rather than guarded by --dry-run (semantic-release
  still runs verifyConditions in dry-run mode, so @semantic-release/github
  would fail without a token). Corrected the dry-run description, which
  wrongly claimed dry-run skips only tag and push.
@nielspardon

Copy link
Copy Markdown
Member Author

Do we really need the RELEASE_DRY_RUN environment variable […] in addition to the standard --dry-run command-line argument?

Yes — this is a deliberate safety mechanism copied from the spec repo (substrait-io/substrait#1099), and --dry-run alone isn't sufficient. semantic-release still runs every plugin's verifyConditions step in dry-run mode; it skips only prepare, publish, addChannel, success, and fail. @semantic-release/github's verifyConditions throws ENOGHTOKEN without a GITHUB_TOKEN, so a plain --dry-run with the full plugin set can't produce a credential-free preview. RELEASE_DRY_RUN lets us omit the side-effecting plugins entirely (and defaults to dry-run as a fail-safe, so a forgotten/typo'd var stays harmless) rather than merely guarding them behind --dry-run.

My dry run confirms this end to end — it completes token-free precisely because those plugins are dropped:

✔  Completed step "verifyConditions" of plugin "@semantic-release/exec"
✔  Completed step "verifyConditions" of plugin "@semantic-release/changelog"
ℹ  The next release version is 0.95.1
⚠  Skip step "prepare"  … in dry-run mode
⚠  Skip step "publish"  … in dry-run mode
⚠  Skip step "success"  … in dry-run mode

I've restored the comment explaining this (dropped in the port) and corrected it — the original claimed dry-run "only skips tag and push," which isn't accurate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants